import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;


public class IDSModelBuilderLogic {
	
	/*
	 * fields
	 */
	
	private LinkedList<Event> eventList;//event list sorted by time
	private LinkedList<String> uniqueEventNameList;//!!!unique event name list
	private LinkedList<LinkedList<Event>> listForEventListInTimeBin;// eventlist for each time bin
	private LinkedList<LinkedList<Integer>> listForEventFreqScoreInTimeBin;//!!!score for each time bin; the order is same as uniqueEventNameList
	private LinkedList<LinkedList<Integer>> listForEventOccurrenceInTimeBin;
	
	private LinkedHashMap<String, Integer> mapForNGramScore;//!!!
	
	/*
	 * constructor
	 */
	public IDSModelBuilderLogic(LinkedList<Event> eventList){
		this.eventList = eventList;
		createUniqueEventNameList();
		createListForEventListInTimeBin();
		createListForEventScoreInTimeBin();
		
		createMapForNGramScore();
	}

	/*
	 * methods
	 */
	
	private void createUniqueEventNameList(){
		this.uniqueEventNameList = new LinkedList<String>();
		Iterator<Event> ite = this.eventList.iterator();
		while(ite.hasNext()){
			Event eve = ite.next();
			String processName = eve.getProcessName();
			if(!this.uniqueEventNameList.contains(processName)){
				this.uniqueEventNameList.add(processName);
			}
		}
	}

	private void createListForEventListInTimeBin(){
		//init
		this.listForEventListInTimeBin = new LinkedList<LinkedList<Event>>();
		for(int i=0;i<24;i++){
			LinkedList<Event> eventList = new LinkedList<Event>();
			this.listForEventListInTimeBin.add(i, eventList);
		}
		
		//iterate eventList
		Iterator<Event> ite = this.eventList.iterator();
		while(ite.hasNext()){
			Event event = ite.next();
			int hour = event.getTimeStamp().getTime().getHours();
			LinkedList<Event> eventListInTheTimeBin = this.listForEventListInTimeBin.get(hour);
			eventListInTheTimeBin.add(event);
		}
	}
	
	private void createListForEventScoreInTimeBin(){
		//init
		this.listForEventOccurrenceInTimeBin = new LinkedList<LinkedList<Integer>>();
		for(int i=0;i<24;i++){
			LinkedList<Integer> occurrenceList = new LinkedList<Integer>();
			for(int j=0;j<uniqueEventNameList.size();j++){
				occurrenceList.add(0);
			}
			this.listForEventOccurrenceInTimeBin.add(i,occurrenceList);
		}
		
		//count occurrence
		for(int i=0;i<24;i++){
			LinkedList<Integer> occurrenceList = this.listForEventOccurrenceInTimeBin.get(i);
			LinkedList<Event> eventList2 = this.listForEventListInTimeBin.get(i);
			
			Iterator<Event> ite = eventList2.iterator();
			while(ite.hasNext()){
				Event eve = ite.next();
				String procName = eve.getProcessName();
				int index = this.uniqueEventNameList.indexOf(procName);
				int tmp = occurrenceList.get(index);
				occurrenceList.set(index, tmp+1);
			}
		}
		
		//init
		this.listForEventFreqScoreInTimeBin = new LinkedList<LinkedList<Integer>>();
		for(int i=0;i<24;i++){
			LinkedList<Integer> scoreList = new LinkedList<Integer>();
			for(int j=0;j<uniqueEventNameList.size();j++){
				scoreList.add(0);
			}
			this.listForEventFreqScoreInTimeBin.add(i,scoreList);
		}
		
		//calculate score
		for(int i=0;i<24;i++){
			LinkedList<Integer> occurrenceList = this.listForEventOccurrenceInTimeBin.get(i);
			LinkedList<Integer> scoreList = this.listForEventFreqScoreInTimeBin.get(i);
			//get total
			int totalNumberOfEvents = 0;
			Iterator<Integer> ite1 = occurrenceList.iterator();
			while(ite1.hasNext()){
				int occurrence = ite1.next();
				totalNumberOfEvents = totalNumberOfEvents + occurrence;
			}
			//cal score
			for(int j=0;j<occurrenceList.size();j++){
				int occurrence = occurrenceList.get(j);
				int score = 0;
				if(totalNumberOfEvents!=0){
					score = (int)(occurrence*Const.CONST_MULTIPLE_TIMES/totalNumberOfEvents);
				}
				else{
					score = 0;
				}
				scoreList.set(j, score);
			}
		}
	}

	public void writeListForEventFreqScoreInTimeBin(String outputFileName){
		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(outputFileName));
			
			bw.write("*,");
			for(int i=0;i<this.uniqueEventNameList.size();i++){
				bw.write(this.uniqueEventNameList.get(i)+",");
			}
			bw.newLine();
			
			for(int i=0;i<this.listForEventFreqScoreInTimeBin.size();i++){
				LinkedList<Integer> scoreList = this.listForEventFreqScoreInTimeBin.get(i);
				bw.write(i+":00-"+(i+1)+":00,");
				for(int j=0;j<scoreList.size();j++){
					bw.write(scoreList.get(j)+",");
				}
				bw.newLine();
			}
			bw.flush();
			bw.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
	
	public void writeListForEventOccurrenceInTimeBin(String outputFileName){
		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(outputFileName));
			
			bw.write("*,");
			for(int i=0;i<this.uniqueEventNameList.size();i++){
				bw.write(this.uniqueEventNameList.get(i)+",");
			}
			bw.newLine();
			
			for(int i=0;i<this.listForEventOccurrenceInTimeBin.size();i++){
				LinkedList<Integer> scoreList = this.listForEventOccurrenceInTimeBin.get(i);
				bw.write(i+":00-"+(i+1)+":00,");
				for(int j=0;j<scoreList.size();j++){
					bw.write(scoreList.get(j)+",");
				}
				bw.newLine();
			}
			bw.flush();
			bw.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
	
	
	public void createMapForNGramScore(){
		
		Map<String, Integer> mapForNGram = new LinkedHashMap<String, Integer>();		
		for(int i=0;i< this.eventList.size()-1;i++){
			Event event1 = eventList.get(i);
			Event event2 = eventList.get(i+1);
			String key = event1.getProcessName() + ":" + event2.getProcessName();
			if(mapForNGram.containsKey(key)){
				int numberOfNGram = mapForNGram.get(key);
				mapForNGram.put(key, numberOfNGram+1);
			}
			else{
				mapForNGram.put(key, 1);
			}
		}
		
		mapForNGramScore = new LinkedHashMap<String, Integer>();
		int allSize = this.eventList.size();
		Iterator ite = mapForNGram.entrySet().iterator();
		while(ite.hasNext()){
			Map.Entry entry = (Map.Entry)ite.next();
			String key = (String)entry.getKey();
			int numberOfNGram = (Integer)entry.getValue();
			int score = (int)((numberOfNGram*Const.CONST_MULTIPLE_TIMES)/allSize);
			if(score==0){
				score = 1;
			}
			this.mapForNGramScore.put(key, score);
		}
	}

	public void writeMapForNGramScore(String outputFileName){
		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(outputFileName));
			//write first line
			bw.write("NGram,Score");
			bw.newLine();
			Iterator ite = this.mapForNGramScore.entrySet().iterator();
			while(ite.hasNext()){
				Map.Entry entry = (Map.Entry)ite.next();
				String key = (String)entry.getKey();
				int score = (Integer)entry.getValue();
				bw.write(key+","+score);
				bw.newLine();
			}
			bw.flush();
			bw.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	
	public LinkedList<String> getUniqueEventNameList(){
		return this.uniqueEventNameList;
	}

	public LinkedList<LinkedList<Integer>> getListForEventScoreInTimeBin(){
		return this.listForEventFreqScoreInTimeBin;
	}
	
	public LinkedHashMap<String, Integer> getMapForNGramScore(){
		return this.mapForNGramScore;
	}
	
	
	

	
}
